﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using ICSharpCode.AvalonEdit.Rendering;
using ICSharpCode.AvalonEdit.Utils;
using Oxage.Common;

namespace ICSharpCode.AvalonEdit.Editing
{
	public class BookmarkColumnMargin : AbstractMargin, IBookmarkColumn
	{
		#region Constructor
		public BookmarkColumnMargin()
		{
			bookmarks = new ObservableCollection<IBookmark>();
			bookmarks.CollectionChanged += (sender, e) => Redraw();
		}
		#endregion

		#region Protected methods
		/// <summary>
		/// Gets a line number by mouse position.
		/// </summary>
		/// <param name="e"></param>
		/// <returns></returns>
		protected int GetLineNumber(MouseEventArgs e)
		{
			if (base.TextView == null)
			{
				return 0;
			}

			var line = base.TextView.GetVisualLineFromVisualTop(e.GetPosition(base.TextView).Y + base.TextView.ScrollOffset.Y);
			if (line == null)
			{
				return 0;
			}

			return line.FirstDocumentLine.LineNumber;
		}
		#endregion

		#region Public methods
		/// <summary>
		/// Gets a bookmark by line number.
		/// </summary>
		/// <param name="lineNumber"></param>
		/// <returns></returns>
		public IBookmark GetBookmark(int lineNumber)
		{
			var bookmark =
				from x in this.Bookmarks
				where x.LineNumber == lineNumber
				orderby x.ZIndex descending
				select x;

			return bookmark.FirstOrDefault();
		}

		public void Redraw()
		{
			InvalidateVisual();
		}
		#endregion

		#region Visual methods
		/// <inheritdoc/>
		protected override HitTestResult HitTestCore(PointHitTestParameters hitTestParameters)
		{
			//Accept clicks even when clicking on the background
			return new PointHitTestResult(this, hitTestParameters.HitPoint);
		}

		/// <inheritdoc/>
		protected override Size MeasureOverride(Size availableSize)
		{
			return new Size(18, 0);
		}

		protected override void OnRender(DrawingContext drawingContext)
		{
			Size renderSize = this.RenderSize;

			//Background
			//var background = SystemColors.ControlBrush;
			var background = new SolidColorBrush(Color.FromArgb(0xFF, 0x44, 0x44, 0x44));
			drawingContext.DrawRectangle(background, null, new Rect(0, 0, renderSize.Width, renderSize.Height));

			//Vertical line
			//drawingContext.DrawLine(new Pen(SystemColors.ControlDarkBrush, 1), new Point(renderSize.Width - 0.5, 0), new Point(renderSize.Width - 0.5, renderSize.Height));

			TextView textView = base.TextView;
			if (textView != null && textView.VisualLinesValid)
			{
				//Create a dictionary line number => first bookmark
				Dictionary<int, IBookmark> bookmarkDict = new Dictionary<int, IBookmark>();
				foreach (IBookmark bm in this.Bookmarks)
				{
					int line = bm.LineNumber;
					IBookmark existingBookmark;
					if (!bookmarkDict.TryGetValue(line, out existingBookmark) || bm.ZIndex > existingBookmark.ZIndex)
					{
						bookmarkDict[line] = bm;
					}
				}

				//Draw bookmarks with the highest ZIndex
				Size pixelSize = PixelSnapHelpers.GetPixelSize(this);
				foreach (VisualLine line in textView.VisualLines)
				{
					int lineNumber = line.FirstDocumentLine.LineNumber;

					IBookmark bm;
					if (bookmarkDict.TryGetValue(lineNumber, out bm))
					{
						Rect rect = new Rect(0, PixelSnapHelpers.Round(line.VisualTop - textView.VerticalOffset, pixelSize.Height), 16, 16);
						drawingContext.DrawImage(bm.Image, rect);
					}
				}
			}
		}

		protected override void OnMouseDown(MouseButtonEventArgs e)
		{
			base.OnMouseDown(e);

			if (e.ChangedButton == MouseButton.Left)
			{
				//Prevent text selecting from this point
				e.Handled = true;
			}
		}

		protected override void OnMouseUp(MouseButtonEventArgs e)
		{
			base.OnMouseUp(e);
			int line = GetLineNumber(e);
			if (!e.Handled && e.ChangedButton == MouseButton.Left && line > 0)
			{
				OnToggleBookmarkRequest(line);
			}
		}

		/// <inheritdoc/>
		protected override void OnTextViewChanged(TextView oldTextView, TextView newTextView)
		{
			//Detach event from previous TextView
			if (oldTextView != null)
			{
				oldTextView.VisualLinesChanged -= (sender, e) => { };
			}

			base.OnTextViewChanged(oldTextView, newTextView);

			//Attach event to the new TextView
			if (newTextView != null)
			{
				newTextView.VisualLinesChanged += (sender, e) => Redraw();
			}

			Redraw();
		}
		#endregion

		#region Bubble events
		public event EventHandler<EventArgs<int>> ToggleBookmarkRequest;
		protected virtual void OnToggleBookmarkRequest(int lineNumber)
		{
			if (ToggleBookmarkRequest != null)
			{
				ToggleBookmarkRequest(this, new EventArgs<int>(lineNumber));
			}
		}
		#endregion

		#region IBookmarkColumn Members
		public IList<IBookmark> Bookmarks
		{
			get
			{
				return bookmarks;
			}
		}
		private ObservableCollection<IBookmark> bookmarks;
		#endregion
	}
}
